﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;

namespace Stock.Model
{
    public interface IBaseUnitOfWork
    {
        /// <summary>
        /// 将操作提交到数据库，
        /// </summary>
        void Save();

        Task SaveAsync();

        /// <summary>
        /// 是否不提交到数据库，这只是在具体的repository类中的SaveChanges方法里用到的
        /// 默认为false，即默认为提交到数据库
        /// </summary>
        /// <returns></returns>
        bool IsNotSubmit { get; set; }

        bool Commit();


    }

    public interface IUnitOfWork:IBaseUnitOfWork
    {
        XTContext Context { get;  }

        int ExecuteSqlCommand(string sql, params object[] parameters);

        IRepository<TEntity> GetRepository<TEntity>() where TEntity : BaseEntity;

        IQueryable<TEntity> FromSql<TEntity>(string sql, params object[] parameters) where TEntity : class;
    }

	/// <summary>
	/// 工作单元
	/// 对泛型类型的支持
	/// </summary>
	/// <typeparam name="T"></typeparam>
    public interface IUnitWork<T> : IBaseUnitOfWork where T : DbContext {
    
        T Context { get; }
    }

    public class UnitWork<T> : IUnitWork<T> where T:DbContext
    {
        public T _context;
        public T Context
        {
            get { return _context; }
        }

        public UnitWork(string DBType, string connection)
        {
			var optionsBuilder = new DbContextOptionsBuilder<T>();

			switch (DBType)
			{
				case "SQLSERVER":
					optionsBuilder.UseSqlServer(connection);
					break;
				case "MYSQL":
					optionsBuilder.UseMySql(connection);
					break;
				case "POSTGRESQL":
					optionsBuilder.UseNpgsql(connection);
					break;
			}

            _context = (T)System.Activator.CreateInstance(typeof(T), optionsBuilder);
        }

		public void Save()
		{
			_context.SaveChanges();
		}

		public async Task SaveAsync()
		{
			await _context.SaveChangesAsync();
		}

		public bool IsNotSubmit { get; set; }

		public bool Commit()
		{
			return _context.SaveChanges() > 0;
		}



		public void Dispose()
		{
			if (_context != null)
			{
				_context.Dispose();
			}
			GC.SuppressFinalize(this);
		}
    }

    public class UnitOfWork:IUnitOfWork
    {
        public XTContext _context;

        public XTContext Context => _context;

        private Dictionary<Type, object> repositories;

        private bool disposed = false;

        public UnitOfWork( string DBType, string connection)
        {
			var optionsBuilder = new DbContextOptionsBuilder<XTContext>();

			switch (DBType)
			{
				case "SQLSERVER":
					optionsBuilder.UseSqlServer(connection);
					break;
				case "MYSQL":
					optionsBuilder.UseMySql(connection);
					break;
				case "POSTGRESQL":
					optionsBuilder.UseNpgsql(connection);
					break;
			}

            _context = new XTContext(optionsBuilder.Options);
		}

		/// <summary>
		/// Gets the specified repository for the <typeparamref name="TEntity"/>.
		/// </summary>
		/// <typeparam name="TEntity">The type of the entity.</typeparam>
		/// <returns>An instance of type inherited from <see cref="IRepository{TEntity}"/> interface.</returns>
		public IRepository<TEntity> GetRepository<TEntity>() where TEntity : BaseEntity
		{
			if (repositories == null)
			{
				repositories = new Dictionary<Type, object>();
			}

			var type = typeof(TEntity);
			if (!repositories.ContainsKey(type))
			{
				repositories[type] = new Repository<TEntity>(_context);
			}

			return (IRepository<TEntity>)repositories[type];
		}

		/// <summary>
		/// Executes the specified raw SQL command.
		/// </summary>
		/// <param name="sql">The raw SQL.</param>
		/// <param name="parameters">The parameters.</param>
		/// <returns>The number of state entities written to database.</returns>
		public int ExecuteSqlCommand(string sql, params object[] parameters) => _context.Database.ExecuteSqlCommand(sql, parameters);


		/// <summary>
		/// Uses raw SQL queries to fetch the specified <typeparamref name="TEntity" /> data.
		/// </summary>
		/// <typeparam name="TEntity">The type of the entity.</typeparam>
		/// <param name="sql">The raw SQL.</param>
		/// <param name="parameters">The parameters.</param>
		/// <returns>An <see cref="IQueryable{T}" /> that contains elements that satisfy the condition specified by raw SQL.</returns>
		public IQueryable<TEntity> FromSql<TEntity>(string sql, params object[] parameters) where TEntity : class => _context.Set<TEntity>().FromSql(sql, parameters);


		public void Save()
        {
            _context.SaveChanges();
        }

        public async Task SaveAsync()
        {
            await _context.SaveChangesAsync();
        }

		public bool IsNotSubmit { get; set; }

		public bool Commit()
         {
             return _context.SaveChanges() > 0;
         }

		public void Dispose()
		{
			Dispose(true);

			GC.SuppressFinalize(this);
		}

		/// <summary>
		/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
		/// </summary>
		/// <param name="disposing">The disposing.</param>
		protected virtual void Dispose(bool disposing)
		{
			if (!disposed)
			{
				if (disposing)
				{
					// clear repositories
					if (repositories != null)
					{
						repositories.Clear();
					}

					// dispose the db context.
					_context.Dispose();
				}
			}

			disposed = true;
		}
    }
}
